﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Diagnostics;
using GenericLibrary.Database;


namespace GenericLibrary.Xml {

    public class XmlDbDataReader : XmlReader {

        enum State { Initial, Error, Query, QueryEnd, Table, TableEnd, Row, RowEnd, Cell, CellEnd, CellValue, Closed, CellBaseTable, CellBaseColumn };

        private string _baseUri, _queryName;
        private string[] _tableNames;
        private DbDataReader _dbDataReader;
        private int _columnOrdinal, _resultSetOrdinal;
        private XmlNameTable _nameTable;
        private State _state;

        private void Initialize( Database.Database database, string sql, string baseUri, string queryName, string[] tableNames ) {
            try {
                this._dbDataReader = database.Read( sql );
                this._queryName = queryName;
                this._tableNames = tableNames;
                this._columnOrdinal = -1;
                this._resultSetOrdinal = 0;
                this._baseUri = baseUri;
                this._nameTable = new NameTable();
                this._nameTable.Add( this._baseUri );
                this._state = State.Initial;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XmlDbDataReader( string baseUri, Database.Database database, string sql, string queryName, params string[] tableNames ) {
            try {
                this.Initialize( database, sql, baseUri, queryName, tableNames );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XmlDbDataReader( Database.Database database, string sql, string queryName, params string[] tableNames ) {
            try {
                this.Initialize( database, sql, string.Empty, queryName, tableNames );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public override int AttributeCount {
            get {
                throw new Exception( "The method or operation is not implemented." );
            }
        }

        public override string BaseURI {
            get {
                return this._baseUri;
            }
        }

        public override void Close() {
            try {
                if( !this._dbDataReader.IsClosed ) {
                    this._dbDataReader.Close();
                }
                this._state = State.Closed;
            } catch( Exception ) {
                Debug.Assert( false );
                this._state = State.Error;
                throw;
            }
        }

        public override int Depth {
            get {
                try {
                    switch( this._state ) {
                        case State.Initial:
                            return 0;
                        case State.Query:
                            return 1;
                        case State.Table:
                            return 2;
                        case State.Row:
                            return 3;
                        case State.Cell:
                            return 4;
                        case State.CellValue:
                            return 5;
                        default:
                            throw new Exception();
                    }
                } catch( Exception ) {
                    Debug.Assert( false );
                    this._state = State.Error;
                    throw;
                }
            }
        }

        public override bool EOF {
            get {
                return this._state == State.QueryEnd;
            }
        }

        public override string GetAttribute( int i ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override string GetAttribute( string name, string namespaceURI ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override string GetAttribute( string name ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool HasValue {
            get {
                //return this.NodeType == XmlNodeType.Text;
                throw new Exception( "The method or operation is not implemented." );
            }
        }

        public override bool IsEmptyElement {
            get {
                return false;
            }
        }

        public override string LocalName {
            get {
                try {
                    string localName = null;
                    switch( this._state ) {
                        case State.Query:
                            localName = this._queryName;
                            break;
                        case State.Table:
                        case State.Row:
                            if( this._tableNames != null && this._resultSetOrdinal < this._tableNames.Length ) {
                                localName = this._tableNames[ this._resultSetOrdinal ];
                            }
                            if( localName == null || localName.Length == 0 ) {
                                localName = string.Format( "Table{0}", this._resultSetOrdinal );
                            }
                            break;
                        case State.Cell:
                            localName = this._dbDataReader.GetName( this._columnOrdinal );
                            break;
                        case State.CellBaseTable:
                            localName = "BaseTableName";
                            break;
                        case State.CellBaseColumn:
                            localName = "BaseColumnName";
                            break;
                        default:
                            localName = string.Empty;
                            break;
                    }
                    this._nameTable.Add( localName );
                    return localName;
                } catch( Exception ) {
                    Debug.Assert( false );
                    this._state = State.Error;
                    throw;
                }
            }
        }

        public override string LookupNamespace( string prefix ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool MoveToAttribute( string name, string ns ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool MoveToAttribute( string name ) {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool MoveToElement() {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool MoveToFirstAttribute() {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override bool MoveToNextAttribute() {
            try {
                switch( this._state ) {
                    case State.Cell:
                        this._state = State.CellBaseTable;
                        return true;
                    case State.CellBaseTable:
                        this._state = State.CellBaseColumn;
                        return true;
                    case State.CellBaseColumn:
                        this._state = State.Cell;
                        return false;
                    default:
                        return false;
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public override XmlNameTable NameTable {
            get { return this._nameTable; }
        }

        public override string NamespaceURI {
            get { return string.Empty; }
        }

        public override XmlNodeType NodeType {
            get {
                try {
                    switch( this._state ) {
                        case State.Query:
                        case State.Table:
                        case State.Row:
                        case State.Cell:
                            return XmlNodeType.Element;
                        case State.QueryEnd:
                        case State.TableEnd:
                        case State.RowEnd:
                        case State.CellEnd:
                            return XmlNodeType.EndElement;
                        case State.CellValue:
                            return XmlNodeType.Text;
                        case State.CellBaseTable:
                        case State.CellBaseColumn:
                            return XmlNodeType.Attribute;
                        default:
                            return XmlNodeType.None;
                    }
                } catch( Exception ) {
                    Debug.Assert( false );
                    this._state = State.Error;
                    throw;
                }
            }
        }

        public override string Prefix {
            get { return string.Empty; }
        }

        public override bool Read() {
            try {
                switch( this._state ) {
                    case State.Initial:
                        this._state = State.Query;
                        return true;
                    case State.Query:
                        this._state = State.Table;
                        return true;
                    case State.QueryEnd:
                        return false;
                    case State.Table:
                        if( this._dbDataReader.Read() ) {
                            this._state = State.Row;
                        } else {
                            this._state = State.TableEnd;
                        }
                        return true;
                    case State.TableEnd:
                        if( this._dbDataReader.NextResult() ) {
                            this._resultSetOrdinal++;
                            this._state = State.Table;
                        } else {
                            this._state = State.QueryEnd;
                        }
                        return true;
                    case State.Row:
                        this._columnOrdinal = 0;
                        this._state = State.Cell;
                        return true;
                    case State.RowEnd:
                        if( this._dbDataReader.Read() ) {
                            this._state = State.Row;
                        } else {
                            this._state = State.TableEnd;
                        }
                        return true;
                    case State.Cell:
                        if( this._dbDataReader.IsDBNull( this._columnOrdinal ) ) {
                            this._state = State.CellEnd;
                        } else {
                            this._state = State.CellValue;
                        }
                        return true;
                    case State.CellEnd:
                        if( this._columnOrdinal + 1 < this._dbDataReader.FieldCount ) {
                            this._columnOrdinal++;
                            this._state = State.Cell;
                        } else {
                            this._state = State.RowEnd;
                        }
                        return true;
                    case State.CellValue:
                        this._state = State.CellEnd;
                        return true;
                    case State.Error:
                        return false;
                    default:
                        throw new Exception();
                }
            } catch( Exception ) {
                Debug.Assert( false );
                this._state = State.Error;
                throw;
            }
        }

        public override bool ReadAttributeValue() {
            throw new Exception( "The method or operation is not implemented." );
        }

        public override ReadState ReadState {
            get {
                try {
                    switch( this._state ) {
                        case State.Initial:
                            return ReadState.Initial;
                        case State.Error:
                            return ReadState.Error;
                        case State.QueryEnd:
                            return ReadState.EndOfFile;
                        case State.Closed:
                            return ReadState.Closed;
                        default:
                            return ReadState.Interactive;
                    }
                } catch( Exception ) {
                    Debug.Assert( false );
                    this._state = State.Error;
                    throw;
                }
            }
        }

        public override void ResolveEntity() {
            throw new Exception( "The method or operation is not implemented." );
        }

        //state dependant
        public override string Value {
            get {
                try {
                    switch( this._state ) {
                        case State.CellValue:
                            return this._dbDataReader[ this._columnOrdinal ].ToString();
                        case State.CellBaseTable:
                            return Convert.ToString( this._dbDataReader.GetSchemaTable().Rows[ this._columnOrdinal ][ "BaseTableName" ] );
                        case State.CellBaseColumn:
                            return Convert.ToString( this._dbDataReader.GetSchemaTable().Rows[ this._columnOrdinal ][ "BaseColumnName" ] );
                        default:
                            return string.Empty;
                    }
                } catch( Exception ) {
                    Debug.Assert( false );
                    this._state = State.Error;
                    throw;
                }
            }
        }
    }

    

}
